package com.bugbuff.sso.service;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.dubbo.config.annotation.Reference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bugbuff.common.mapper.UserMapper;
import com.bugbuff.common.pojo.User;
import com.bugbuff.dubbo.server.api.HuanXinApi;
import com.bugbuff.dubbo.server.pojo.FrozenUser;
import com.bugbuff.dubbo.server.pojo.Logs;
import com.bugbuff.sso.vo.UserVo;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 业务层实现类
 */
@Service
@Slf4j
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Value("${jwt.secret}")
    private String secret;

    //    注入RedisTemplate
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Reference
    private HuanXinApi huanXinApi;

    /*public ErrorResult login(String phone) {
        String redisKey = "CHECK_CODE_" + phone;
        Boolean flag = redisTemplate.hasKey(redisKey);
        if (flag) {
            return ErrorResult.builder().errCode("000001").errMessage("验证码还未失效！").build();
        }

//        2、发行验证码
        String code = "123456";
//        String code = sendSms(phone);

//        3、将发送成功的验证码存储到redis
        redisTemplate.opsForValue().set(redisKey, code, Duration.ofMinutes(5));
        return null;

    }*/

    public UserVo loginVerification(String phone, String code) {
        String redisKey = "CHECK_CODE_" + phone;
//        1、从Redis中获取验证码
        String redisCode = redisTemplate.opsForValue().get(redisKey);
//        2、与code比较
        if (!redisCode.equals(code)) {//        2.1、验证码不一致，登录失败
//            return ErrorResult.builder().errCode("000002").errMessage("验证码不一致！").build();
            return null;
        }
        //删除redis中的验证码
        redisTemplate.delete(redisKey);

//        2.2、一致则表示通过
//        3、校验手机号，查询数据库
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>();
        wrapper.eq(User::getMobile, phone);
        User user = userMapper.selectOne(wrapper);

        boolean isNew = false;
//        3.1、如果数据库中不存在，则表示为新用户，需要注册
        if (user == null) {
            user = new User();
            user.setMobile(phone);
            user.setPassword(DigestUtils.md5Hex("123456"));
            userMapper.insert(user);
//            isNew 改为true表示为新用户
            isNew = true;

            //注册环信用户
            Boolean result = this.huanXinApi.register(user.getId());
            if (!result) {
                //注册环信失败，记录日志
                log.error("注册环信用户失败~ userId = " + user.getId());
            }
        }
//        3.2、否则，是老用户
        /*String frozenUserRedisKey = "FROZEN_USER_" + user.getId();
        if (redisTemplate.opsForHash().hasKey(frozenUserRedisKey, "1") || isPermanentFrozen(user.getId()))
            return UserVo.builder().token("-1").build();*/

//        生成token
//        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI4MjkzMzYsInVzZXJfaWQiOiIxIn0.Mbzn6LzsLrkVWEbhexR3lTYDZjxqIcqW11rJxDQ6Ewk";

//        将手机号和用户id绑定
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("mobile", user.getMobile());
        claims.put("id", user.getId());

        // 生成token
        String token = Jwts.builder()
                .setClaims(claims) //payload，存放数据的位置，不能放置敏感数据，如：密码等
                .signWith(SignatureAlgorithm.HS256, secret) //设置加密方法和加密盐
                .setExpiration(new DateTime().plusHours(12).toDate()) //设置过期时间，12小时后过期
                .compact();

        //创建日志对象
        Logs logs = new Logs();
        //记录用户登录时间
        logs.setLoginTime(System.currentTimeMillis());
        //记录用户uid
        logs.setUid(user.getId());
        //记录用户登录设备
        String[] deviceList = {"手机","平板电脑","笔记本电脑","台式电脑"};
        String device = RandomUtil.randomEle(deviceList);
        logs.setDevice(device);
        //记录用户登录地点
        String[] locationList = {"合肥","南京","北京","上海","杭州","苏州"};
        String location = RandomUtil.randomEle(locationList);
        logs.setLandingLocation(location);
        //记录用户登出时间
        Long[] longs ={3600000L,7200000L,10800000L,5400000L,5687421L,1584687L};
        Long randomTime = RandomUtil.randomEle(longs);
        logs.setLogoutTime(System.currentTimeMillis() + randomTime);
        //记录用户访问类型
        logs.setType("01");
        mongoTemplate.save(logs);

        return UserVo.builder().token(token).isNew(isNew).build();
    }

    /**
     * 通过token解析数据
     *
     * @param token
     * @return
     */
    public User queryUserByToken(String token) {
        try {
            // 通过token解析数据
            Map<String, Object> body = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();

            User user = new User();
            user.setId(Long.valueOf(body.get("id").toString()));

            //需要返回user对象中的mobile，需要查询数据库获取到mobile数据
            //如果每次都查询数据库，必然会导致性能问题，需要对用户的手机号进行缓存操作
            //数据缓存时，需要设置过期时间，过期时间要与token的时间一致
            //如果用户修改了手机号，需要同步修改redis中的数据

            String redisKey = "TANHUA_USER_MOBILE_" + user.getId();
            if (this.redisTemplate.hasKey(redisKey)) {
                String mobile = this.redisTemplate.opsForValue().get(redisKey);
                user.setMobile(mobile);
            } else {
                //查询数据库
                User u = this.userMapper.selectById(user.getId());
                user.setMobile(u.getMobile());

                //将手机号写入到redis中
                //在jwt中的过期时间的单位为：秒
                long timeout = Long.valueOf(body.get("exp").toString()) * 1000 - System.currentTimeMillis();
                this.redisTemplate.opsForValue().set(redisKey, u.getMobile(), timeout, TimeUnit.MILLISECONDS);
            }

            log.info("解析的token为：{}", token);

            return user;
        } catch (ExpiredJwtException e) {
            log.info("token已经过期！ token = " + token);
        } catch (Exception e) {
            log.error("token不合法！ token = " + token, e);
        }
        return null;
    }

}
